<template>
	<div class="klotski">
		<div class="klotski_detail">
			<p class="klotski_detail_title">数字华容道</p>
			<div class="type_change">
				<el-button type="success" @click="changeType(3, '初级')"
					>初级模式</el-button
				>
				<el-button type="warning" @click="changeType(4, '中级')"
					>中级模式</el-button
				>
				<el-button type="danger" @click="changeType(5, '高级')"
					>高级模式</el-button
				>
			</div>
			<div class="detail_one">
				<p class="detail_one_p">
					<span class="detail_one_p_span">当前模式:</span>
					<span class="detail_one_p_span1">{{ type }}</span>
				</p>
				<p class="detail_one_p">
					<span class="detail_one_p_span">进行步骤:</span>
					<span class="detail_one_p_span1">{{ steps }}</span>
				</p>
			</div>
			<div class="detail_center" v-if="!isSuccess">
				<div
					v-for="(item, y) in randList"
					:key="item[0]"
					class="klotski_all"
					:style="{ height: `${100 / latitude}%` }"
				>
					<div class="klotski_item">
						<div
							v-for="(one, x) in item"
							:key="one"
							class="klotski_item_one"
							@click="clickFun(x, y)"
							:class="{ noShow: one == board }"
						>
							<p v-show="one != board" class="klotski_item_one_p">{{ one }}</p>
						</div>
					</div>
				</div>
			</div>
			<div class="detail_success" v-else>
				<p class="detail_success_title">🎉 太棒啦 🎉</p>
				<p class="detail_success_cent">
					恭喜小伙伴仅仅使用{{ steps }}步就成功通过{{ type }}模式，太厉害啦🌹
				</p>
				<div class="detail_success_btn">
					<el-button @click="changeType(latitude, type)" plain
						>再次挑战</el-button
					>
					<el-button
						v-if="type != '高级'"
						type="primary"
						plain
						@click="changeType(latitude, type)"
						>下一关卡</el-button
					>
				</div>
			</div>
			<div class="play_detail">
				<p class="play_detail_title">玩法说明：</p>
				<p class="play_detail_cent">
					<span class="detail_cent_one">操作过程:</span>
					<span class="detail_cent_two"
						>点击数字，如果旁边有空格则会和空格位置互换</span
					>
				</p>
				<p class="play_detail_cent">
					<span class="detail_cent_one">达成结果:</span>
					<span class="detail_cent_two"
						>每次移动一个数字，将完全打乱的数字按照顺序复原</span
					>
				</p>
			</div>
		</div>
	</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
type OrderArrTypr = number[];
interface Movetype {
	top: boolean;
	right: boolean;
	bottom: boolean;
	left: boolean;
}
let latitude = ref<number>(3); // 维度
const board = ref(latitude.value * latitude.value); // 棋盘格子
const randList = ref<OrderArrTypr[]>([]); // 棋盘坐标
const steps = ref<number>(0); // 记录步数
const type = ref<string>("初级"); // 记录模式
const isSuccess = ref<boolean>(false); // 挑战是否成功
onMounted(() => {
	makeBoardFun();
});
const changeType = (value: number, index: string) => {
	isSuccess.value = false;
	latitude.value = value;
	board.value = value * value;
	type.value = index;
	makeBoardFun();
};
/**
 * 点击棋子
 * @param x 纵坐标
 * @param y 横坐标
 */
const clickFun = (x: number, y: number) => {
	steps.value = steps.value + 1; // 步数累加
	if (randList.value[y][x] == 0) return;
	moveFun(isMoveFun(x, y, randList.value), x, y, randList.value);
	const isEnd = judgeSuccessFun(latitude.value, randList.value);
	if (isEnd) {
		isSuccess.value = true;
	}
};
/**
 * 判断是否完成游戏
 */
const judgeSuccessFun = (n: number, data: OrderArrTypr[]) => {
	let index = 0;
	while (index < n) {
		for (let i = 0; i < n; i++) {
			let nowIndex = data[index][i] - n * index;
			if (nowIndex != i + 1) {
				return false;
			}
		}
		index = index + 1;
	}
	return true;
};
/**
 * 判断是否是边界
 * @param x 所处横坐标
 * @param y 所处纵坐标
 * @param n 边界
 * @returns 边界情况
 */
const isBorderFun = (x: number, y: number, n: number) => {
	const borderObj = {
		top: false,
		right: false,
		bottom: false,
		left: false,
	};
	if (x == 0) {
		// 位于左边界
		borderObj.left = true;
	} else if (x == n - 1) {
		// 位于右边界
		borderObj.right = true;
	}
	if (y == 0) {
		// 位于上边界
		borderObj.top = true;
	} else if (y == n - 1) {
		// 位于下边界
		borderObj.bottom = true;
	}
	return borderObj;
};
/**
 * 判断是否能移动
 * @param x 当前元素的横坐标
 * @param y 当前元素的纵坐标
 * @param data 棋盘数据
 */
const isMoveFun = (x: number, y: number, data: OrderArrTypr[]) => {
	let moveObj = {
		top: false,
		right: false,
		bottom: false,
		left: false,
	};
	// 获取边界情况
	const borderObj = isBorderFun(x, y, latitude.value);
	// 说明不在上边界，判断是否向上移动
	if (!borderObj.top) {
		if (data[y - 1][x] == board.value) {
			moveObj.top = true;
			return moveObj;
		}
	}
	// 说明不在下边界，判断是否向下移动
	if (!borderObj.bottom) {
		if (data[y + 1][x] == board.value) {
			moveObj.bottom = true;
			return moveObj;
		}
	}
	// 说明不在左边界，判断是否向上移动
	if (!borderObj.left) {
		if (data[y][x - 1] == board.value) {
			moveObj.left = true;
			return moveObj;
		}
	}
	// 说明不在右边界，判断是否向上移动
	if (!borderObj.right) {
		if (data[y][x + 1] == board.value) {
			moveObj.right = true;
			return moveObj;
		}
	}
	// 说明不能移动
	return moveObj;
};
/**
 * 移动
 */
const moveFun = (
	moveObj: Movetype,
	x: number,
	y: number,
	data: OrderArrTypr[]
) => {
	if (moveObj.top) {
		[data[y][x], data[y - 1][x]] = [data[y - 1][x], data[y][x]];
	}
	if (moveObj.right) {
		[data[y][x], data[y][x + 1]] = [data[y][x + 1], data[y][x]];
	}
	if (moveObj.bottom) {
		[data[y][x], data[y + 1][x]] = [data[y + 1][x], data[y][x]];
	}
	if (moveObj.left) {
		[data[y][x], data[y][x - 1]] = [data[y][x - 1], data[y][x]];
	}
};
/**
 * 生成棋盘
 */
const makeBoardFun = () => {
	// 重置步数
	steps.value = 0;
	randList.value = markMultidimensional(
		upsetOrderlyArrFun(markContinuousFun(board.value)),
		latitude.value
	);
};
/**
 * 生成一个连续填充数组
 * @param length 数组长度
 * @returns 连续填充数组
 */
const markContinuousFun = (length: number) => {
	return Array.from({ length }).map((item, index) => {
		return index + 1;
	});
};
/**
 * 打乱一个有序数组
 * @param data 有序数组
 * @returns 乱序数组
 */
const upsetOrderlyArrFun = (data: OrderArrTypr) => {
	// Fisher-Yates 洗牌算法
	for (let i = 0; i < data.length; ++i) {
		const j = Math.floor(Math.random() * (data.length - i)) + i;
		[data[i], data[j]] = [data[j], data[i]];
	}
	return data;
};
/**
 * 生成一个N维数组
 * @param data 目标数组
 * @param n 维数
 * @returns N维数组
 */
const markMultidimensional = (data: OrderArrTypr, n: number) => {
	const arr = [];
	while (data.length > 0) {
		let newArr = data.splice(0, n);
		arr.push(newArr);
	}
	return arr;
};
</script>
<style scoped lang="less">
.klotski {
	width: 100%;
	height: 100%;
	position: relative;
	overflow: auto;

	@media screen and(min-width: 1000px) {
		.klotski_detail {
			width: 500px;
			height: 750px;
			position: absolute;
			top: 50%;
			left: 50%;
			transform: translate(-50%, -50%);
			background-color: rgb(229, 181, 157);
			border-radius: 5%;
			box-shadow: 5px 5px 10px 5px rgb(206, 211, 232);
			.klotski_detail_title {
				height: 80px;
				line-height: 80px;
				color: rgb(126, 76, 59);
				font-size: 30px;
				text-align: center;
			}
			.type_change {
				display: flex;
				align-items: center;
				justify-content: center;
			}
			.detail_one {
				display: flex;
				justify-content: center;
				align-items: center;
				padding-top: 20px;
				.detail_one_p {
					margin: 0 10px;
					.detail_one_p_span {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 500;
						color: #4a4a4a;
						margin-right: 10px;
					}
					.detail_one_p_span1 {
						font-size: 20px;
						font-family: PingFang SC;
						font-weight: 400;
						color: rgb(252, 249, 245);
					}
				}
			}
			.detail_center {
				width: 400px;
				height: 400px;
				padding: 5px;
				background-color: rgb(156, 121, 71);
				margin: 20px auto 0;
				.klotski_all {
					height: 100%;
					.klotski_item {
						height: 100%;
						display: flex;
						.klotski_item_one {
							flex: 1;
							display: flex;
							align-items: center;
							justify-content: center;
							font-size: 24px;
							cursor: pointer;

							.klotski_item_one_p {
								width: 95%;
								height: 95%;
								background-color: rgb(221, 167, 101);
								border-radius: 10%;
								display: flex;
								align-items: center;
								justify-content: center;
								color: white;
								font-size: 34px;
							}
						}
						.noShow {
							background-color: transparent;
						}
					}
				}
			}
			.detail_success {
				width: 400px;
				height: 400px;
				padding: 5px;
				background-color: rgb(156, 121, 71);
				margin: 20px auto 0;
				.detail_success_title {
					text-align: center;
					color: rgb(255, 113, 88);
					font-size: 28px;
					margin: 60px 0 30px 0;
				}
				.detail_success_cent {
					color: rgb(245, 237, 224);
					font-size: 20px;
					text-align: center;
					padding: 0 30px;
				}
				.detail_success_btn {
					margin-top: 60px;
					text-align: center;
				}
			}
			.play_detail {
				width: 100%;
				height: 100px;
				padding: 0 20px;
				margin-top: 30px;
				.play_detail_title {
					font-size: 20px;
					font-weight: 400;
					color: white;
				}
				.play_detail_cent {
					margin-top: 10px;
					.detail_cent_one {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 500;
						color: #4a4a4a;
						margin-right: 10px;
					}
					.detail_cent_two {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 400;
						color: black;
					}
				}
			}
		}
	}
	@media screen and(max-width: 1000px) {
		.klotski_detail {
			width: 100vw;
			height: 100%;
			position: absolute;
			top: 50%;
			left: 50%;
			transform: translate(-50%, -50%);
			background-color: rgb(229, 181, 157);
			box-shadow: 5px 5px 10px 5px rgb(206, 211, 232);
			overflow: auto;
			.klotski_detail_title {
				height: 10vh;
				line-height: 10vh;
				color: rgb(126, 76, 59);
				font-size: 30px;
				text-align: center;
			}
			.type_change {
				display: flex;
				align-items: center;
				justify-content: center;
			}
			.detail_one {
				display: flex;
				justify-content: center;
				align-items: center;
				padding-top: 20px;
				.detail_one_p {
					margin: 0 10px;
					.detail_one_p_span {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 500;
						color: #4a4a4a;
						margin-right: 10px;
					}
					.detail_one_p_span1 {
						font-size: 20px;
						font-family: PingFang SC;
						font-weight: 400;
						color: rgb(252, 249, 245);
					}
				}
			}
			.detail_center {
				width: 90vw;
				height: 90vw;
				padding: 5px;
				background-color: rgb(156, 121, 71);
				margin: 20px auto 0;
				.klotski_all {
					height: 100%;
					.klotski_item {
						height: 100%;
						display: flex;
						.klotski_item_one {
							flex: 1;
							display: flex;
							align-items: center;
							justify-content: center;
							font-size: 24px;
							cursor: pointer;

							.klotski_item_one_p {
								width: 95%;
								height: 95%;
								background-color: rgb(221, 167, 101);
								border-radius: 10%;
								display: flex;
								align-items: center;
								justify-content: center;
								color: white;
								font-size: 34px;
							}
						}
						.noShow {
							background-color: transparent;
						}
					}
				}
			}
			.detail_success {
				width: 90vw;
				height: 90vw;
				padding: 5px;
				background-color: rgb(156, 121, 71);
				margin: 20px auto 0;
				.detail_success_title {
					text-align: center;
					color: rgb(255, 113, 88);
					font-size: 28px;
					margin: 60px 0 30px 0;
				}
				.detail_success_cent {
					color: rgb(245, 237, 224);
					font-size: 20px;
					text-align: center;
					padding: 0 30px;
				}
				.detail_success_btn {
					margin-top: 60px;
					text-align: center;
				}
			}
			.play_detail {
				width: 100%;
				height: 100px;
				padding: 0 20px;
				margin-top: 30px;
				.play_detail_title {
					font-size: 20px;
					font-weight: 400;
					color: white;
				}
				.play_detail_cent {
					margin-top: 10px;
					.detail_cent_one {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 500;
						color: #4a4a4a;
						margin-right: 10px;
					}
					.detail_cent_two {
						font-size: 16px;
						font-family: PingFang SC;
						font-weight: 400;
						color: black;
					}
				}
			}
		}
	}
}
</style>
